﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace CurveAdjustPlugin
{
    public partial class CurveControl : UserControl
    {

        CatmullSpline spline = new CatmullSpline();

        public event EventHandler CurveChanged = null;

        private void OnCurveChanged()
        {
            if (CurveChanged != null)
                CurveChanged(this, new EventArgs());
        }

        public CurveControl()
        {
            InitializeComponent();
            Init();
        }

        private void Init()
        {
            bitBuf = new Bitmap(Width, Height);   
        }

        Bitmap bitBuf = null;

        private void CurveControl_Paint(object sender, PaintEventArgs e)
        {
            Redraw();
            Graphics g = CreateGraphics();
            g.DrawImageUnscaled(bitBuf, 0, 0);
        }

        public override void Refresh()
        {
            
            Invalidate(new Rectangle(0,0,1,1));
        }

        int selectedIndex = -1;

        private void Redraw()
        {
            using (Graphics g = Graphics.FromImage(bitBuf))
            {
                g.FillRectangle(new SolidBrush(BackColor), ClientRectangle);

                for (int i = 1; i < 10; i++)
                {
                    Pen dashPen = new Pen(Color.Gray, 1.0f);
                    dashPen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dot;
                    g.DrawLine(dashPen, 0, i / 10.0f * ClientRectangle.Height, ClientRectangle.Width, i / 10.0f * ClientRectangle.Height);
                    g.DrawLine(dashPen, i / 10.0f * ClientRectangle.Width, 0, i / 10.0f * ClientRectangle.Width, ClientRectangle.Height);

                }
                g.DrawRectangle(Pens.Gray, ClientRectangle.Left, ClientRectangle.Top, ClientRectangle.Width-1,
                     ClientRectangle.Height-1);

                List<PointF> points = spline.GetSamplePoints(50);
                int lstScreenX = 0;
                int lstScreenY = (ClientRectangle.Height - 1) - (int)Math.Round(CatmullSpline.GetValue(0.0f, points) * (ClientRectangle.Height - 1));
                
                for (int i = 1; i <= 255; i++)
                {
                    float x = i / 255.0f;
                    int screenY = (ClientRectangle.Height - 1) - (int)Math.Round(CatmullSpline.GetValue(x, points) * (ClientRectangle.Height - 1));
                    int screenX = (int)Math.Round(x * (ClientRectangle.Width-1));
                    g.DrawLine(Pens.Black, lstScreenX, lstScreenY, screenX, screenY);
                    lstScreenY = screenY;
                    lstScreenX = screenX;
                }
                for (int i = 0; i < spline.GetCtrlPointsCount(); i++)
                {
                    PointF pt = spline.GetCtrlPoint(i);
                    int px = (int)(pt.X*(ClientRectangle.Width-1));
                    int py = (ClientRectangle.Height-1) - (int)(pt.Y * (ClientRectangle.Height - 1));
                    Rectangle pointRect = new Rectangle(px - 4, py - 4, 8, 8);
                    g.DrawRectangle(Pens.Black, pointRect);
                    if (i == selectedIndex)
                    {
                        g.FillRectangle(Brushes.Black, pointRect);
                    }
                }
            }
        }

        private void CurveControl_Resize(object sender, EventArgs e)
        {
            Init();
        }

        bool mouseDown = false;

        private void CurveControl_MouseDown(object sender, MouseEventArgs e)
        {
            PointF hitPoint = new PointF(e.X/(float)Width, 1.0f - e.Y/(float)Height);
            selectedIndex = spline.FindNearbyControlPoint(hitPoint.X, hitPoint.Y, 8.0f / 255.0f);
            mouseDown = true;
            if (selectedIndex == -1)
            {
                hitPoint.Y = Math.Max(0.0f, hitPoint.Y);
                hitPoint.Y = Math.Min(1.0f, hitPoint.Y);
                spline.AddCtrlPoint(hitPoint);
                selectedIndex = spline.FindNearbyControlPoint(hitPoint.X, hitPoint.Y, 8.0f / 255.0f);
                OnCurveChanged();
            }
            Refresh();
        }

        private void CurveControl_MouseMove(object sender, MouseEventArgs e)
        {
            PointF hitPoint = new PointF(e.X / (float)Width, 1.0f - e.Y / (float)Height);
            if (mouseDown && selectedIndex != -1)
            {
                hitPoint.X = Math.Max(spline.GetCtrlPointMinX(selectedIndex), hitPoint.X);
                hitPoint.X = Math.Min(spline.GetCtrlPointMaxX(selectedIndex), hitPoint.X);
                hitPoint.Y = Math.Max(0.0f, hitPoint.Y);
                hitPoint.Y = Math.Min(1.0f, hitPoint.Y);
                hitPoint.X = Math.Max(0.0f, hitPoint.X);
                hitPoint.X = Math.Min(1.0f, hitPoint.X);
                spline.SetCtrlPoint(selectedIndex, hitPoint);
                OnCurveChanged();
                Refresh();
            }
            else
            {
                int n = spline.FindNearbyControlPoint(hitPoint.X, hitPoint.Y, 8.0f / 255.0f);
                if (n != -1)
                {
                    Cursor = Cursors.SizeAll;
                }
                else
                    Cursor = Cursors.Cross;
            }
            
        }

        private void CurveControl_MouseUp(object sender, MouseEventArgs e)
        {
            mouseDown = false;
        }

        private void CurveControl_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Delete)
            {
                DeleteControlPoint();
            }
        }

        public void DeleteControlPoint()
        {
            if (selectedIndex != -1 && selectedIndex > 0 && selectedIndex < spline.GetCtrlPointsCount()-1)
            {
                spline.DeleteCtrlPoint(selectedIndex);
                selectedIndex = -1;
                OnCurveChanged();
                Refresh();
            }
        }

        public Byte[] GetColorMap()
        {
            Byte[] rs = new Byte[256];
            List<PointF> points = spline.GetSamplePoints(50);
            for (int i = 0; i < 256; i++)
            {
                float v = i / 255.0f;
                rs[i] = (byte)Math.Round(255*CatmullSpline.GetValue(v, points));
            }
            return rs;
        }
        
    }
}
